#pragma once

#include<iostream>
#include<queue>
#include<cstdlib>
#include<unistd.h>
#include<pthread.h>

using namespace std;

const uint32_t gDefaultCap = 5;

template<class T>
class BlockQueue
{
public:
    BlockQueue(uint32_t cap = gDefaultCap)
    :_cap(cap),_bq(cap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_conCond,nullptr);
        pthread_cond_init(&_proCond,nullptr);
      
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_conCond);
        pthread_cond_destroy(&_proCond);
    }

    //生产接口
    void push(const T &in)//纯输入
    {
        lockQueue();//加锁
        if(isFull())//不满足不生产。休眠
        {
            proBlockwait();//阻塞等待,等待被唤醒
        }

        pushCore(in);//满足条件生产
        unlockQueue();//解锁

        wakeupCon();//唤醒消费者

    }

    //消费接口
    const T pop(T &out)//输出
    {
        lockQueue();//加锁
        if(isEmpty())
        {
            conBlockwait();//阻塞等待,等待被唤醒
        }
        //条件满足
        T tmp = popCore();;

        unlockQueue();//解锁

        wakeupPro();//唤醒生产者


        return tmp;
    }

private:
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }

    bool isEmpty()
    {
        return _bq.size();
    }

    bool isFull()
    {
        return _bq.size() == _cap;
    }

    void proBlockwait()
    {
        //在阻塞线程的时候自动释放锁
        pthread_cond_wait(&_proCond,&_mutex);
    }

    void conBlockwait()
    {
        //在阻塞线程的时候自动释放锁
        pthread_cond_wait(&_proCond,&_mutex);
    }

    void wakeupCon()//唤醒消费者
    {
        pthread_cond_signal(&_proCond);
    }

    void wakeupPro()//唤醒生产者
    {
        pthread_cond_signal(&_conCond);
    }

    void pushCore(T &in)
    {
        _bq.push(in);
    }

    T popCore()
    {
        T tmp = _bq.front();
        _bq.pop();
        return tmp;
    }
    

private:
    uint32_t _cap;//容量
    queue<T> _bq;//blockqueue 
    pthread_mutex_t _mutex;//保护堵塞队列的互斥索
    pthread_cond_t _conCond;//让消费者等待的条件变量
    pthread_cond_t _proCond;//让生产者等待的条件变量

};

